#include "queue.h"
#include "errorcode.h"
#include "include.h"

cls_queue::cls_queue()
{
    m_queue		= NULL;
    m_maxsize	= 0;
    m_leftsize	= 0;
    m_head		= 0;
    m_tail		= 0;

    _INIT_LOCK( &m_lock );
}

cls_queue::~cls_queue()
{
    release();
    _DEL_LOCK( &m_lock );
}

/**
 *	initialize the queue object
 *
 * 	@size		[in] the queue size
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t	cls_queue::instance( pem_u32_t size )
{
    pem_error_t		res;

    _LOCK( &m_lock );

    res = _instance( size );

    _UNLOCK( &m_lock );

    return res;
}

/**
 *	release the queue
 */
pem_void_t cls_queue::release()
{
    _LOCK( &m_lock );

    if ( NULL != m_queue )
    {
        delete [] m_queue;
        m_queue = NULL;
    }

    m_maxsize	= 0;
    m_leftsize	= 0;
    m_head		= 0;
    m_tail		= 0;

    _UNLOCK( &m_lock );
}

/**
 *	check the queue instance status
 */
pem_bool_t cls_queue::is_instance( )
{
    pem_bool_t		ok;

    _LOCK( &m_lock );

    ok = (NULL != m_queue);

    _UNLOCK( &m_lock );

    return ok;
}

/**
 *	put data into the queue
 *
 * 	@pdata			[in] the data buffer
 * 	@size			[in] the data buffer size
 * 	@flag			[in] the way put data into the queue ( IN_AFTER or IN_BEFORE )
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t cls_queue::putdata( pem_s8_t* pdata, pem_u32_t size, pem_s32_t flag )
{
    pem_error_t		res;

    if ( NULL == pdata || 0 == size )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_PARAMS );

    _LOCK( &m_lock );

    res = _putdata( pdata, size, flag );

    _UNLOCK( &m_lock );

    return res;
}

/**
 *	get data from the queue
 *
 * 	@pdata		[in] the buffer to load the data
 * 	@size		[in] the size of the data that want to be load into the buffer
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t cls_queue::getdata( pem_s8_t* pdata, pem_u32_t size )
{
    pem_error_t		res;

    if ( NULL == pdata || 0 == size )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_PARAMS );

    _LOCK( &m_lock );

    res = _getdata( pdata, size );

    _UNLOCK( &m_lock );

    return 	res;
}

/**
 *	clear up the queue
 */
pem_error_t cls_queue::setempty()
{
    pem_error_t		res = PEM_OK;

    _LOCK( &m_lock );

    if ( NULL == m_queue )
        res = BUILD_ERRORCODE( 0, PEM_QUEUE_CREATE );
    else
    {
        m_head			= 0;
        m_tail			= 0;
        m_leftsize		= m_maxsize;
    }

    _UNLOCK( &m_lock );

    return	res;
}

/**
 *	initialize the queue object
 *
 * 	@size		[in] the queue size
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t	cls_queue::_instance( pem_u32_t	size )
{
    if ( NULL != m_queue )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_EXIST );

    m_queue = new pem_s8_t[size];
    if ( NULL == m_queue )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_CREATE );

    memset( m_queue, 0, size );
    m_maxsize	= size;
    m_leftsize	= size;
    m_head		= 0;
    m_tail		= 0;

    return 	PEM_OK;
}

/**
 *	put data into the queue
 *
 * 	@pdata			[in] the data buffer
 * 	@size			[in] the data buffer size
 * 	@flag			[in] the way put data into the queue ( IN_AFTER or IN_BEFORE )
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t cls_queue::_putdata( pem_s8_t* pdata, pem_u32_t size, pem_s32_t flag )
{
    if ( NULL == m_queue )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_CREATE );

    if ( size > m_leftsize )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_FULL );

    if ( IN_AFTER == flag )
        __putdata_after( pdata, size );
    else
        __putdata_before( pdata, size );

    return	PEM_OK;
}

/**
 *	put data at the tail of the queue
 *
 * 	@pdata			[in] the data buffer
 * 	@size			[in] the data buffer size
 */
pem_void_t cls_queue::__putdata_after( pem_s8_t* pdata, pem_u32_t size )
{
    if ( m_tail >= m_head )
    {
        if ( m_maxsize - m_tail > size )
        {
            memcpy( m_queue + m_tail, pdata, size );
            m_tail	+= size;
        }
        else if ( m_maxsize - m_tail < size )
        {
            memcpy( m_queue + m_tail, pdata, m_maxsize - m_tail );
            pdata	+= (m_maxsize - m_tail);
            memcpy( m_queue, pdata, size - (m_maxsize - m_tail) );
            m_tail	= (size - (m_maxsize - m_tail));
        }
        else
        {
            memcpy( m_queue + m_tail, pdata, size );
            m_tail	= 0;
        }
    }
    else
    {
        memcpy( m_queue + m_tail, pdata, size );
        m_tail	+= size;
    }

    m_leftsize -= size;
}

/**
 *	put data at the head of the queue
 *
 * 	@pdata			[in] the data buffer
 * 	@size			[in] the data buffer size
 */
pem_void_t cls_queue::__putdata_before( pem_s8_t* pdata, pem_u32_t size )
{
    if ( m_tail >= m_head )
    {
        if ( m_head >= size )
        {
            memcpy( m_queue + m_head - size, pdata, size );
            m_head	-= size;
        }
        else
        {
            memcpy( m_queue + m_maxsize - (size - m_head), pdata, (size - m_head) );
            memcpy( m_queue, pdata + (size - m_head), m_head );
            m_head = m_maxsize - (size - m_head);
        }
    }
    else
    {
        memcpy( m_queue + m_head - size, pdata, size );
        m_head	-= size;
    }

    m_leftsize -= size;
}

/**
 *	get data from the queue
 *
 * 	@pdata		[out] the buffer to load the data
 * 	@size		[in] the size of the data that want to be load into the buffer
 *
 * 	return
 * 		==	0	successful
 * 		!=	0	errorcode
 */
pem_error_t cls_queue::_getdata( pem_s8_t* pdata, pem_u32_t size )
{
    if ( NULL == m_queue )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_CREATE );

    if ( m_maxsize - m_leftsize < size )
        return BUILD_ERRORCODE( 0, PEM_QUEUE_EMPTY );

    __getdata( pdata, size );

    return PEM_OK;
}

/**
 *	get data from the queue
 *
 * 	@pdata		[out] the buffer to load the data
 * 	@size		[in] the size of the data that want to be load into the buffer
 */
pem_void_t cls_queue::__getdata( pem_s8_t* pdata, pem_u32_t size )
{
    if ( m_head >= m_tail )
    {
        if ( m_maxsize - m_head > size )
        {
            memcpy( pdata, m_queue + m_head, size );
            m_head	+= size;
        }
        else if ( m_maxsize - m_head < size )
        {
            memcpy( pdata, m_queue + m_head, m_maxsize - m_head );
            pdata	+= (m_maxsize - m_head);
            memcpy( pdata, m_queue, size -  (m_maxsize - m_head) );
            m_head	= (size - (m_maxsize - m_head));
        }
        else
        {
            memcpy( pdata, m_queue + m_head, size );
            m_head	= 0;
        }
    }
    else
    {
        memcpy( pdata, m_queue + m_head, size );
        m_head	+= size;
    }

    m_leftsize += size;
}
